use std::collections::BTreeMap;
use crate::{error::ServerError, protocol::{
    data_schema::DataTrait, datatypes::SYSTEM_DATA_TYPE, type_schema::TypeSchema::{self, *}
}};
use serde_json::{Value as JSON, json};
use serde::{Serialize, Deserialize};

fn invalid_data_error() -> Result<Data, ServerError> {
    Err(ServerError::DataErrorStr("data format is invalid"))
}

fn invalid_typeschema_error(s: &str) -> Result<Data, ServerError> {
    Err(ServerError::DataError(format!("typeschema format is invalid: {:?}", s)))
}

#[derive(PartialEq, Debug)]
pub enum Data {
    DataVoid,
    DataBool(bool),
    DataInt(i64),
    DataFloat(f64),
    DataStr(String),
    DataJson(JSON),
    DataList(Vec<Data>),
    DataMap(BTreeMap<Data, Data>),
    DataTuple(Vec<Data>),
}

impl PartialOrd for Data {
    fn ge(&self, other: &Self) -> bool {
        true
    }
    fn gt(&self, other: &Self) -> bool {
        true
    }
    fn le(&self, other: &Self) -> bool {
        false
    }
    fn lt(&self, other: &Self) -> bool {
        false
    }
    fn partial_cmp(&self, other: &Self) -> std::prelude::v1::Option<std::cmp::Ordering> {
        None
    }
}

impl Eq for Data {}

impl Ord for Data {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        std::cmp::Ordering::Greater
    }
}

use Data::*;

impl std::fmt::Display for Data {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            DataVoid => write!(f, "null"),
            DataBool(b) => write!(f, "{}", b),
            DataInt(i) => write!(f, "{}", i),
            DataFloat(v) => write!(f, "{}", v),
            DataStr(s) => write!(f, "\"{}\"", s),
            DataJson(j) => write!(f, "#\"{}\"#", j),
            DataList(ls) => {
                write!(f, "[")?;
                for (i, item) in ls.iter().enumerate() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{}", item)?;
                }
                write!(f, "]")
            }
            DataMap(map) => {
                write!(f, "[")?;
                for (i, (k, v)) in map.iter().enumerate() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{{{}, {}}}", k, v)?;
                }
                write!(f, "]")
            }
            DataTuple(ls) => {
                write!(f, "(")?;
                for (i, item) in ls.iter().enumerate() {
                    if i > 0 {
                        write!(f, ", ")?;
                    }
                    write!(f, "{}", item)?;
                }
                write!(f, ")")
            }
        }
    }
}

impl Data {
    pub fn is_empty(&self) -> bool {
        match self {
            DataVoid => true,
            _ => false,
        }
    }
    pub fn as_string(self) -> String {
        match self.try_as_string() {
            Ok(s) => s,
            Err(e) => panic!("data is not a string: {:?}", e)
        }
    }
    pub fn as_vec(self) -> Vec<Data> {
        match self.try_as_vec() {
            Ok(ls) => ls,
            Err(e) => panic!("data is not a list: {:?}", e)
        }
    }
    pub fn try_as_string(self) -> Result<String, ServerError> {
        match self {
            DataStr(s) => Ok(s),
            _ => Err(ServerError::DataErrorStr("data is not a string"))
        }
    }
    pub fn try_as_vec(self) -> Result<Vec<Data>, ServerError> {
        match self {
            DataList(ls) => Ok(ls),
            _ => Err(ServerError::DataErrorStr("data is not a list"))
        }
    }
    pub fn try_as_int(self) -> Result<i64, ServerError> {
        match self {
            DataInt(i) => Ok(i),
            _ => Err(ServerError::DataErrorStr("data is not an integer"))
        }
    }
    pub fn try_as_float(self) -> Result<f64, ServerError> {
        match self {
            DataFloat(f) => Ok(f),
            _ => Err(ServerError::DataErrorStr("data is not a float"))
        }
    }
    pub fn try_as_bool(self) -> Result<bool, ServerError> {
        match self {
            DataBool(b) => Ok(b),
            _ => Err(ServerError::DataErrorStr("data is not a boolean"))
        }
    }
    pub fn try_as_json(self) -> Result<JSON, ServerError> {
        match self {
            DataJson(j) => Ok(j),
            _ => Err(ServerError::DataErrorStr("data is not a json"))
        }
    }
    pub fn try_as_map(self) -> Result<BTreeMap<Data, Data>, ServerError> {
        match self {
            DataMap(map) => Ok(map),
            _ => Err(ServerError::DataErrorStr("data is not a map"))
        }
    }
    pub fn try_as_tuple(self) -> Result<Vec<Data>, ServerError> {
        match self {
            DataTuple(ls) => Ok(ls),
            _ => Err(ServerError::DataErrorStr("data is not a tuple"))
        }
    }    
}

impl Data {
    pub fn to_string(&self) -> String {
        format!("{}", self)
    }

    pub fn to_json_less(&self) -> JSON {
        match self {
            DataVoid => json!(null),
            DataBool(b) => json!(b),
            DataInt(i) => json!(i),
            DataFloat(f) => json!(f),
            DataStr(s) => json!(s),
            DataJson(j) => j.clone(),
            DataList(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_less());
                }
                JSON::Array(arr)
            }
            DataMap(map) => {
                let mut arr = vec![];
                for (k, v) in map {
                    let inner = vec![k.to_json_less(), v.to_json_less()];
                    arr.push(json!(inner));
                }
                JSON::Array(arr)
                
            }
            DataTuple(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_less());
                }
                JSON::Array(arr)
            }
        }
    }

    pub fn to_json_full(&self) -> JSON {
        match self {
            DataVoid => json!(null),
            DataBool(b) => json!(b),
            DataInt(i) => json!(i),
            DataFloat(f) => json!(f),
            DataStr(s) => json!(s),
            DataJson(j) => j.clone(),
            DataList(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_less());
                }
                JSON::Array(arr)
            }
            DataMap(map) => {
                let mut arr = vec![];
                for (k, v) in map {
                    arr.push(json!({
                        "k": k.to_json_full(),
                        "v": v.to_json_full()
                    }));
                }
                JSON::Array(arr)
            }
            DataTuple(ls) => {
                let mut map = serde_json::Map::new();
                for (i, item) in ls.iter().enumerate() {
                    map.insert(i.to_string(), item.to_json_full());
                }
                JSON::Object(map)
            }
        }
    }

    pub fn as_json_less(self) -> JSON {
        match self {
            DataVoid => json!(null),
            DataBool(b) => json!(b),
            DataInt(i) => json!(i),
            DataFloat(f) => json!(f),
            DataStr(s) => json!(s),
            DataJson(j) => j.clone(),
            DataList(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_less());
                }
                JSON::Array(arr)
            }
            DataMap(map) => {
                let mut arr = vec![];
                for (k, v) in map {
                    let inner = vec![k.to_json_less(), v.to_json_less()];
                    arr.push(json!(inner));
                }
                JSON::Array(arr)
                
            }
            DataTuple(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_less());
                }
                JSON::Array(arr)
            }
        }
    }
    
    pub fn as_json_full(self) -> JSON {
        match self {
            DataVoid => json!(null),
            DataBool(b) => json!(b),
            DataInt(i) => json!(i),
            DataFloat(f) => json!(f),
            DataStr(s) => json!(s),
            DataJson(j) => json!({"json": j}),
            DataList(ls) => {
                let mut arr = vec![];
                for item in ls {
                    arr.push(item.to_json_full());
                }
                json!({"list": arr})
            }
            DataMap(map) => {
                let mut arr = vec![];
                for (k, v) in map {
                    arr.push(json!({
                        "k": k.to_json_full(),
                        "v": v.to_json_full()
                    }));
                }
                json!({"map": arr})
            }
            DataTuple(ls) => {
                let mut map = serde_json::Map::new();
                for (i, item) in ls.iter().enumerate() {
                    map.insert(i.to_string(), item.to_json_full());
                }
                json!({"tuple": map})
            }
        }
    }

    pub fn qualify(&self, ts: &TypeSchema) -> bool {
        match (ts, self) {
            (Void, DataVoid) => true,
            (Bool, DataBool(_)) => true,
            (Int, DataInt(_)) => true,
            (Float, DataFloat(_)) => true,
            (Str, DataStr(_)) => true,
            (Json, DataJson(_)) => true,
            (List(sub), DataList(ls)) => ls.iter().all(|item| item.qualify(sub)),
            (Map(sub1, sub2), DataMap(map)) => {
                map.iter().all(|(k, v)| k.qualify(sub1) && v.qualify(sub2))
            }
            (Tuple(ls), DataTuple(ls2)) => {
                ls.iter().zip(ls2.iter()).all(|(a, b)| b.qualify(a))
            }
            (Opt(_), DataVoid) => true,
            (Opt(sub), other) => other.qualify(sub),
            (DataType(name), data) => {
                if let Some(ts) = SYSTEM_DATA_TYPE.explain(name) {
                    data.qualify(&ts)
                }
                else {
                    true // unknown data type always checks (but never fuse)
                }
            }
            _ => false,
        }
    }

    pub fn from_json_less(data: JSON, ts: &TypeSchema) -> Result<Self, ServerError> {
        match (ts, data) {
            (Void, _) => Ok(DataVoid),
            (Bool, JSON::Bool(b)) => Ok(DataBool(b)),
            (Int, JSON::Number(i)) => Ok(DataInt(i.as_i64().expect("json data overflow i64"))),
            (Float, JSON::Number(f)) => Ok(DataFloat(f.as_f64().expect("json data overflow f64"))),
            (Str, JSON::String(s)) => Ok(DataStr(s)),
            (Json, JSON::String(j)) => Ok(DataJson(json!(j))),
            (List(sub), JSON::Array(arr)) => {
                let mut novel: Vec<Data> = vec![];
                for item in arr {
                    novel.push(Data::from_json_less(item, sub)?);
                }
                Ok(DataList(novel))
            }
            (Map(subk, subv), JSON::Array(arr))=> {
                let mut map: BTreeMap<Data, Data> = BTreeMap::new();
                for entry in arr {
                    match entry {
                        JSON::Array(inner) => {
                            let mut idx = 0;
                            let mut k = None;
                            let mut v = None;
                            for each in inner {
                                if idx == 0 {
                                    k = Some(Data::from_json_less(each, subk))
                                }
                                else if idx == 1 {
                                    v = Some(Data::from_json_less(each, subv))
                                }
                                else {
                                    return invalid_data_error();
                                }
                                idx += 1;
                            }
                            if let (Some(kk), Some(vv)) = (k, v) {
                                map.insert(kk?, vv?);
                            }
                            else {
                                return invalid_data_error();
                            }
                        }
                        _ => return invalid_data_error(),
                    }
                }
                Ok(DataMap(map))
            },
            (Tuple(ls), JSON::Array(arr)) => {
                let mut tuple = vec![];
                let mut idx = 0;
                for item in arr {
                    if idx < ls.len() {
                        tuple.push(Data::from_json_less(item, &ls[idx])?);
                    }
                    else {
                        return invalid_data_error();
                    }
                    idx += 1;
                }
                Ok(DataTuple(tuple))
            },
            (Opt(sub), obj) => {
                if obj.is_null() {
                    Ok(DataVoid)
                }
                else {
                    Data::from_json_less(obj, sub)
                }
            },
            (DataType(name), obj) => {
                if let Some(ts) = SYSTEM_DATA_TYPE.explain(name) {
                    Data::from_json_less(obj, ts)
                }
                else {
                    return Err(ServerError::ServiceError(format!("system data type not found: {:?}", name)));
                }
            }
            _ => invalid_data_error()
        }
    }

    pub fn from_json_full(data: JSON) -> Result<Self, ServerError> {
        match data {
            JSON::Null => Ok(DataVoid),
            JSON::Bool(b) => Ok(DataBool(b)),
            JSON::Number(i) => Ok(DataFloat(i.as_f64().expect("json data overflow f64"))),
            JSON::String(s) => Ok(DataStr(s)),
            JSON::Object(mut map) => {
                if let Some(val) = map.remove("list") {
                    match val {
                        JSON::Array(arr) => {
                            let mut ls = vec![];
                            for item in arr {
                                ls.push(Data::from_json_full(item)?);
                            }
                            Ok(DataList(ls))
                        }
                        _ => invalid_data_error()
                    }
                }
                else if let Some(val) = map.remove("map") {
                    match val {
                        JSON::Array(arr) => {
                            let mut map = BTreeMap::new();
                            for item in arr {
                                match item {
                                    JSON::Object(mut inner) => {
                                        let k = inner.remove("k").expect("map key is not found");
                                        let v = inner.remove("v").expect("map value is not found");
                                        map.insert(Data::from_json_full(k)?, Data::from_json_full(v)?);
                                    }
                                    _ => return invalid_data_error()
                                }
                            }
                            Ok(DataMap(map))
                        }
                        _ => return invalid_data_error()
                    }
                }
                else if let Some(val) = map.remove("tuple") {
                    match val {
                        JSON::Object(obj) => {
                            let mut tuple = vec![];
                            for (k, v) in obj {
                                tuple.push(Data::from_json_full(v)?);
                            }
                            Ok(DataTuple(tuple))
                        }
                        _ => return invalid_data_error()
                    }
                }
                else if let Some(val) = map.remove("json") {
                    Ok(DataJson(val))
                }
                else {
                    return invalid_data_error();
                }
            },
            _ => return invalid_data_error()
        }
    }
}

impl Data {
    pub fn try_from_json_less(data: JSON, ts: &TypeSchema) -> Result<Self, ServerError> {
        match (ts, data) {
            (Void, _) => Ok(DataVoid),
            (Bool, JSON::Bool(b)) => Ok(DataBool(b)),
            (Int, JSON::Number(i)) => Ok(DataInt(i.as_i64().expect("json data overflow i64"))),
            (Float, JSON::Number(f)) => Ok(DataFloat(f.as_f64().expect("json data overflow f64"))),
            (Str, JSON::String(s)) => Ok(DataStr(s)),
            (Json, JSON::String(j)) => Ok(DataJson(json!(j))),
            (List(sub), JSON::Array(arr)) => {
                let mut novel: Vec<Data> = vec![];
                for item in arr {
                    novel.push(Data::try_from_json_less(item, sub)?);
                }
                Ok(DataList(novel))
            }
            (Map(subk, subv), JSON::Array(arr))=> {
                let mut map: BTreeMap<Data, Data> = BTreeMap::new();
                for entry in arr {
                    match entry {
                        JSON::Array(inner) => {
                            let mut idx = 0;
                            let mut k = None;
                            let mut v = None;
                            for each in inner {
                                if idx == 0 {
                                    k = Some(Data::try_from_json_less(each, subk)?);
                                }
                                else if idx == 1 {
                                    v = Some(Data::try_from_json_less(each, subv)?);
                                }
                                else {
                                    return invalid_data_error();
                                }
                                idx += 1;
                            }
                            if let (Some(kk), Some(vv)) = (k, v) {
                                map.insert(kk, vv);
                            }
                            else {
                                return invalid_data_error();
                            }
                        }
                        _ => return invalid_data_error(),
                    }
                }
                Ok(DataMap(map))
            },
            (Tuple(ls), JSON::Array(arr)) => {
                let mut tuple = vec![];
                let mut idx = 0;
                for item in arr {
                    if idx < ls.len() {
                        tuple.push(Data::try_from_json_less(item, &ls[idx])?);
                    }
                    else {
                        return invalid_data_error();
                    }
                    idx += 1;
                }
                Ok(DataTuple(tuple))
            }
            _ => invalid_data_error()
        }
    }
    
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_json_less() {
        let json = json!([1, [2, 3], [[4, 5]]]);
        let ts = Tuple(vec![Int, List(Box::new(Int)), Map(Box::new(Int), Box::new(Int))]);
        let data = Data::from_json_less(json.clone(), &ts).unwrap();
        assert_eq!(data.to_json_less(), json);
    }

    fn test_json_full() {
        let json = json!({"tuple": [1, {"list": [2, 3]}, {"map": [[4, 5]]}]});
        let ts = Tuple(vec![Int, List(Box::new(Int)), Map(Box::new(Int), Box::new(Int))]);
        let data = Data::from_json_full(json.clone()).unwrap();
        assert_eq!(data.to_json_full(), json);
    }
    
    
}

